/**
 * Copyright (c) 2018, wuxie All rights reserved.
 * qq:16349023,mail:16349023@qq.com
 * 未经许可禁止任何人通过任何渠道使用、修改源代码.
 * 项目名称 : wxcloud
 *
 * @version V1.0
 */
package cn.xo68.boot.auth.server.web.authorize;

import cn.xo68.boot.auth.core.WebContextUtils;
import cn.xo68.boot.auth.core.common.AuthConstants;
import cn.xo68.boot.auth.core.domain.Oauth2Principal;
import cn.xo68.boot.auth.server.domain.OauthAuthorizationCode;
import cn.xo68.boot.auth.server.domain.OauthClient;
import cn.xo68.boot.auth.server.properties.AuthServerProperties;
import cn.xo68.boot.auth.server.service.OauthAuthorizationCodeService;
import cn.xo68.boot.auth.server.service.OauthClientService;
import cn.xo68.boot.auth.server.service.OauthUserService;
import cn.xo68.boot.auth.server.util.AuthServerUtils;
import cn.xo68.core.date.DateTime;
import cn.xo68.core.util.StringTools;
import org.apache.oltu.oauth2.as.issuer.MD5Generator;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl;
import org.apache.oltu.oauth2.as.request.OAuthAuthzRequest;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.error.OAuthError;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.apache.oltu.oauth2.common.message.types.ResponseType;
import org.apache.oltu.oauth2.common.message.types.TokenType;
import org.apache.oltu.oauth2.common.utils.OAuthUtils;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;

/**
 *  authorize控制器
 * @author wuxie
 * @date 2018/8/5 9:15
 *
 */
@Controller
public class OauthAuthorizeController {

    private static  final Logger logger= LoggerFactory.getLogger(OauthAuthorizeController.class);

    @Autowired
    private AuthServerUtils authServerUtils;

    @Autowired
    private AuthServerProperties authServerProperties;

    @Autowired
    private OauthClientService oauthClientService;


    @Autowired
    private OauthAuthorizationCodeService oauthAuthorizationCodeService;

    @Autowired
    private OauthUserService oauthUserService;

    @GetMapping("/oauth/authorize")
    public Object authorize(Model model, HttpServletRequest request,Subject subject, Oauth2Principal oauth2Principal) throws URISyntaxException, OAuthSystemException{
        try {
            //构建OAuth 授权请求
            OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(request);
            //检查传入的客户端id是否正确，通过查询数据库表oauth2_client比对。
            OauthClient oauthClient = oauthClientService.get(oauthRequest.getClientId());
            //HttpServletResponse.SC_BAD_REQUEST
            if (oauthClient ==null) {
                OAuthResponse oAuthResponse = OAuthASResponse.errorResponse(200)
                        .setError(OAuthError.TokenResponse.INVALID_CLIENT)
                        .buildJSONMessage();

                MediaType mediaType=MediaType.APPLICATION_JSON_UTF8;
                HttpHeaders headers = authServerUtils.buildHeaders(oAuthResponse.getHeaders());
                return ResponseEntity
                        .status(oAuthResponse.getResponseStatus())
                        .headers(headers)
                        .contentType(mediaType)
                        .body(oAuthResponse.getBody());
            }


            //如果用户没有登录，跳转到服务端的登陆页面
            if(!subject.isAuthenticated()) {
                //把当前认证请求参数存储，转到登录页面
//                Map<String,String> queryMap=new HashMap<>();
//                queryMap.put(OAuth.OAUTH_REDIRECT_URI,WebContextUtils.getRequestUrl(request));
//                String loginUrl=WebContextUtils.appendQuery(authServerProperties.getLoginUrl(), queryMap);
                //return "redirect:"+ loginUrl;
                return "forward:"+ authServerProperties.getLoginUrl();
            }

            //String username = (String)subject.getPrincipal();
            //生成授权码
            String authorizationCode = null;
            //responseType目前仅支持CODE，另外还有TOKEN
            String responseType = oauthRequest.getResponseType();

            //进行OAuth响应构建
            OAuthASResponse.OAuthAuthorizationResponseBuilder builder =
                    OAuthASResponse.authorizationResponse(request, HttpServletResponse.SC_FOUND);

            //.getParam(OAuth.OAUTH_RESPONSE_TYPE);
            if (responseType.equals(ResponseType.CODE.toString())) {
                OAuthIssuerImpl oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
                authorizationCode = oauthIssuerImpl.authorizationCode();

                //持久化临时凭证
                OauthAuthorizationCode oauthAuthorizationCode=new OauthAuthorizationCode();
                oauthAuthorizationCode.setAuthorizationCode(authorizationCode);
                oauthAuthorizationCode.setClientId(oauthRequest.getClientId());
                oauthAuthorizationCode.setUserId(oauth2Principal.getUserId());
                oauthAuthorizationCode.setCreateTime(DateTime.Now().getDate());
                oauthAuthorizationCode.setExpireTime(DateTime.Now().addSecond(authServerProperties.getAuthorizeCodeExpireSeconds()).getDate());
                oauthAuthorizationCodeService.insert(oauthAuthorizationCode);

                //设置授权码
                builder.setCode(authorizationCode);
            }else if (responseType.equals(ResponseType.TOKEN.toString())){
                // builder.setAccessToken(oauth2Principal.getAccessToken());
                builder.setParam("param_token", oauth2Principal.getAccessToken());
                builder.setTokenType(TokenType.BEARER.toString());
                builder.setExpiresIn((long) authServerProperties.getTokenExpireSeconds());
            }



            //得到到客户端重定向地址
            String redirectURI = oauthRequest.getRedirectURI();

            //构建响应
            final OAuthResponse response = builder.location(redirectURI).buildQueryMessage();

            String location=response.getLocationUri();
            if(StringTools.isNotEmpty(location) && location.contains("param_token")) {
                location=location.replace("param_token", OAuth.OAUTH_ACCESS_TOKEN);
            }

            //根据OAuthResponse返回ResponseEntity响应
            HttpHeaders headers = new HttpHeaders();
            headers.setLocation(new URI(location));
            return ResponseEntity
                    .status(response.getResponseStatus())
                    .headers(headers)
                    .build();
        } catch (OAuthProblemException e) {

            //出错处理
            String redirectUri = e.getRedirectUri();
            if (OAuthUtils.isEmpty(redirectUri)) {
                //告诉客户端没有传入redirectUri直接报错
                return new ResponseEntity("OAuth callback url needs to be provided by client!!!", HttpStatus.NOT_FOUND);
            }

            //返回错误消息（如?error=）,HttpServletResponse.SC_FOUND
            final OAuthResponse response =
                    OAuthASResponse.errorResponse(HttpServletResponse.SC_FOUND)
                            .error(e).location(redirectUri).buildQueryMessage();
            HttpHeaders headers = new HttpHeaders();
            headers.setLocation(new URI(response.getLocationUri()));
            return new ResponseEntity(headers, HttpStatus.valueOf(response.getResponseStatus()));
        }
    }
}
